#ifndef _DATA_H
#define _DATA_H

#include "sparse_array.h"
#include "sparse_matrix.h"

#include <vector>

#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/multi_array.hpp>

class BaseClique
{
public:
	BaseClique();
	virtual ~BaseClique();

	virtual void read(std::istream &in, int features) = 0;
	virtual SparseArrayPtr labelling(const int x, const int y) const = 0;
	const std::pair<int,int> &gold() const { return m_gold; }

	std::pair<int,int> labels() const 
	{ return std::pair<int,int>(m_first_labels, m_second_labels); }

	virtual void labelling(SparseArrayPtr sa, int x) = 0;
	void gold(const std::pair<int,int> &g) { m_gold = g; }
	virtual void dimensions(int &x, int &y) const
	{
		x = m_first_labels;
		y = m_second_labels;
	}

protected:
	int m_first_labels;
	int m_second_labels;
	std::pair<int,int> m_gold;
	boost::shared_array<SparseArrayPtr> m_features;
};
typedef boost::shared_ptr<BaseClique> BaseCliquePtr;
typedef boost::multi_array<BaseCliquePtr, 2> BaseCliqueArray2d;

class Clique : public BaseClique
{
public:
	Clique();
	Clique(size_t first_labels, size_t second_labels);
	Clique(const Clique &c);

	virtual void read(std::istream &in, int num_features);
	static void skip(std::istream &in);
	virtual SparseArrayPtr labelling(const int x, const int y) const 
	{ 
		assert(x < m_first_labels && y < m_second_labels);
		assert(m_features != 0);
		return m_features[x*m_second_labels+y]; 
	}
	virtual void labelling(SparseArrayPtr sa, int x) { m_features[x] = sa; }
};
typedef boost::shared_ptr<Clique> CliquePtr;

struct Node
{
	Node() : gold(-1) {}
	std::vector<SparseArrayPtr> features;
	int gold;
	void read(std::istream &in, int num_features);
	static void skip(std::istream &in);
};
typedef boost::shared_ptr<Node> NodePtr;
typedef boost::multi_array<NodePtr, 2> NodeArray2d;

struct Event
{
	Event(size_t, size_t);
	Event(std::istream &in, int features);

	size_t order;
	size_t length;
	std::vector<std::vector<BaseCliquePtr> > cliques;
	std::vector<std::vector<NodePtr> > nodes;

	static void skip(std::istream &in);
};
typedef boost::shared_ptr<Event> EventPtr;

class Events 
{
friend std::ostream& operator<<(std::ostream&, const Events&);

public:
	Events();
	virtual ~Events();

	virtual int events() const { return m_events.size(); }
	virtual int features() const { return m_features; }
	virtual int cliques() const { return m_cliques; }
	virtual int nodes() const { return m_nodes; }

	virtual EventPtr event(int i);
	EventPtr next();

	virtual bool read(const std::string &file, bool disable_mpi=false);

protected:
	bool m_init;
	int m_features, m_cliques, m_nodes;
	std::vector<EventPtr> m_events;
	std::map<int,int> m_context_features;
	int m_mpi_start, m_mpi_end;
	int m_curr_event;
};
typedef boost::shared_ptr<Events> EventsPtr;

class FlatEvents : public Events
{
public:
	FlatEvents();
	~FlatEvents();

	int events() const { return m_num_events; }
	int labels() const { return m_num_labels; }
	int label(int i) const { return m_labels[i]; }
	bool read(const std::string &file, bool disable_mpi=false);

	void
	event(long row, int &length, int **features, double **values)
	{ return m_matrix->get_row(row,length,features,values); }

	boost::shared_array<double>
	product(boost::shared_array<double> v) 
	{ return m_matrix->vector_product(v); }

	double
	product(double *v, int x)
	{ return m_matrix->vector_product_row(v,x); }

	boost::shared_array<double>
	transpose_product(boost::shared_array<double> v) 
	{ return m_matrix->vector_product_transpose(v); }

protected:
	long process_events(std::istream &in, bool add_to_matrix=false);

protected:
	bool m_init;
	int m_num_events, m_num_labels;
	SparseMatrixPtr m_matrix;
	std::vector<int> m_labels;
};
typedef boost::shared_ptr<FlatEvents> FlatEventsPtr;

inline std::ostream& operator<<(std::ostream &os, Events &sm)
{
	os << sm.cliques() << " " << sm.events() << " " << sm.features() << std::endl;
	for (int e=0; e < 1 /*sm.events()*/; ++e)
	{
		std::cout << "Event " << e << std::endl;
		EventPtr event = sm.event(e);
		for (int n=0; n < (int)event->length; ++n)
		{
			os << "---------------" << n << "------------------" << std::endl;
			for (int o=0; (n-o) >= 0 && o < (int)event->order; ++o)
			{
				int start = n-o;
				os << "\t-------" << start << "->" << n << "----------" << std::endl;
				NodePtr node = event->nodes[o][n-o];
				for (size_t i=0; i < node->features.size(); ++i)
				{
					if (node->gold == (int)i) os << "*";
					os << "\t" << i << " " << *(node->features[i]) << std::endl;
				}
				if (start > 0)
				{
					BaseCliquePtr clique = event->cliques[o][n-1-o];
					int first_labels=0, second_labels=0;
					clique->dimensions(first_labels,second_labels);
					for (int i=0; i < first_labels; ++i)
						for (int j=0; j < second_labels; ++j)
							os << "\t" << i  << "-" << j << " " 
								<< *(clique->labelling(i,j)) << std::endl;
				}
				os << "\t-------" << start << "->" << n << "----------" << std::endl;
			}
			os << "---------------" << n << "------------------" << std::endl;
		}
		os << std::endl;
	}

	return os;
}


#endif // _DATA_H
